package com.sikaryofficial.workbench.manager;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sikaryofficial.common.core.constant.CacheBusinessConstants;
import com.sikaryofficial.common.core.domain.R;
import com.sikaryofficial.common.core.exception.ServiceException;
import com.sikaryofficial.common.core.utils.PageUtil;
import com.sikaryofficial.common.core.utils.StringUtils;
import com.sikaryofficial.common.core.web.domain.PageResult;
import com.sikaryofficial.common.redis.service.RedisService;
import com.sikaryofficial.common.security.utils.SecurityUtils;
import com.sikaryofficial.system.api.RemoteUserService;
import com.sikaryofficial.system.api.domain.SysUser;
import com.sikaryofficial.system.api.model.goods.GoodsTasteResp;
import com.sikaryofficial.workbench.config.WorkBenchGoodsConfig;
import com.sikaryofficial.workbench.constant.GoodsConstant;
import com.sikaryofficial.workbench.constant.GoodsDescTypeEnum;
import com.sikaryofficial.workbench.constant.GoodsListingStatusEnum;
import com.sikaryofficial.workbench.constant.GoodsMaterialTypeEnum;
import com.sikaryofficial.workbench.constant.GoodsStyleEnum;
import com.sikaryofficial.workbench.constant.RedisCacheKey;
import com.sikaryofficial.workbench.domain.dto.req.goods.CreateOrUpdateGoodsReq;
import com.sikaryofficial.workbench.domain.dto.req.goods.GoodsFilterReq;
import com.sikaryofficial.workbench.domain.dto.req.goods.GoodsListReq;
import com.sikaryofficial.workbench.domain.dto.req.goods.GoodsManageListReq;
import com.sikaryofficial.workbench.domain.dto.req.goods.GoodsMaterialReq;
import com.sikaryofficial.workbench.domain.dto.req.goods.UpdateGoodsStatusReq;
import com.sikaryofficial.workbench.domain.dto.resp.goods.GoodsDTO;
import com.sikaryofficial.workbench.domain.dto.resp.goods.GoodsFilterDTO;
import com.sikaryofficial.workbench.domain.dto.resp.goods.GoodsManageDetailDTO;
import com.sikaryofficial.workbench.domain.dto.resp.goods.GoodsManageListDTO;
import com.sikaryofficial.workbench.domain.entity.Goods;
import com.sikaryofficial.workbench.domain.entity.GoodsDesc;
import com.sikaryofficial.workbench.domain.entity.GoodsMaterial;
import com.sikaryofficial.workbench.domain.entity.GoodsOperateLog;
import com.sikaryofficial.workbench.domain.entity.GoodsRelation;
import com.sikaryofficial.workbench.domain.entity.PriceTier;
import com.sikaryofficial.workbench.domain.entity.U8Product;
import com.sikaryofficial.workbench.domain.eo.GoodsEO;
import com.sikaryofficial.workbench.domain.eo.GoodsTasteEO;
import com.sikaryofficial.workbench.domain.eo.PriceTierEO;
import com.sikaryofficial.workbench.domain.mapping.GoodsDescMapping;
import com.sikaryofficial.workbench.domain.mapping.GoodsMapping;
import com.sikaryofficial.workbench.domain.mapping.GoodsMaterialMapping;
import com.sikaryofficial.workbench.domain.mapping.GoodsRelationMapping;
import com.sikaryofficial.workbench.domain.mapping.PriceTierMapping;
import com.sikaryofficial.workbench.domain.vo.goods.GoodsBrand;
import com.sikaryofficial.workbench.domain.vo.goods.GoodsCategory;
import com.sikaryofficial.workbench.domain.vo.goods.GoodsProduct;
import com.sikaryofficial.workbench.service.IGoodsDescService;
import com.sikaryofficial.workbench.service.IGoodsMaterialService;
import com.sikaryofficial.workbench.service.IGoodsOperateLogService;
import com.sikaryofficial.workbench.service.IGoodsRelationService;
import com.sikaryofficial.workbench.service.IGoodsService;
import com.sikaryofficial.workbench.service.IPriceTierService;
import com.sikaryofficial.workbench.service.impl.IU8ProductServiceImpl;
import com.sikaryofficial.workbench.utils.GoodsUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author : qinjinyuan
 * @desc : 商品管理聚合服务
 * @date : 2024/09/23 17:04
 */
@Service
@Slf4j
@SuppressWarnings(value = "unchecked")
public class GoodsManager {
    @Autowired
    private IGoodsService goodsService;
    @Autowired
    private IGoodsRelationService goodsRelationService;
    @Autowired
    private IGoodsMaterialService goodsMaterialService;
    @Autowired
    private IGoodsDescService goodsDescService;
    @Autowired
    private IGoodsOperateLogService goodsOperateLogService;
    @Autowired
    private IPriceTierService priceTierService;
    @Autowired
    private IU8ProductServiceImpl productService;
    @Autowired
    private RedisService redisService;
    @Resource
    private WorkBenchGoodsConfig workBenchGoodsConfig;
    @Autowired
    private RemoteUserService remoteUserService;

    /**
     * 商品分类树结构查询
     *
     * @return
     */
    public Map<String, List<GoodsDTO>> scopeTree() {
        log.info("Starting to build the goods category tree");
        List<Goods> goodsList = fetchGoodsList();
        List<GoodsDTO> goodsDTOList = convertToDTOList(goodsList);
        Map<String, List<GoodsDTO>> result = groupByStyleAndBrandNo(goodsDTOList);
        log.info("Goods category tree built successfully");
        return result;
    }

    private List<Goods> fetchGoodsList() {
        return goodsService.list(new LambdaQueryWrapper<Goods>()
                .eq(Goods::getDeletedVersion, 0L)
                .eq(Goods::getListingStatus, GoodsListingStatusEnum.YES.getCode())
        );
    }

    private List<GoodsDTO> convertToDTOList(List<Goods> goodsList) {
        List<GoodsDTO> goodsDTOList = GoodsMapping.INSTANCE.coverToDTOList(goodsList);
        return goodsDTOList == null ? Collections.emptyList() : goodsDTOList;
    }

    private Map<String, List<GoodsDTO>> groupByStyleAndBrandNo(List<GoodsDTO> goodsDTOList) {
        return goodsDTOList.stream()
                .filter(item -> Objects.nonNull(item.getStyleAndBrandNo()) && !item.getStyleAndBrandNo().isEmpty())
                .collect(Collectors.groupingBy(GoodsDTO::getStyleAndBrandNo, TreeMap::new, Collectors.toList()));
    }

    public List<GoodsCategory> navigation() {
        String navigationCacheKey = buildNavigationCacheKey(SecurityUtils.getUserId());
        List<GoodsCategory> navigationList = redisService.getCacheObject(navigationCacheKey);
        if (CollUtil.isNotEmpty(navigationList)) {
            return navigationList;
        }
        // 查询数据并添加缓存
        List<Goods> list = goodsService.navigation(SecurityUtils.getUserId());

        List<GoodsCategory> finalNavigationList = doBuildNavigation(list);
        // 设值
        List<GoodsCategory> finalNavigationList1 = buildResultDataByEnum(finalNavigationList);
        if (CollUtil.isEmpty(finalNavigationList)) {
            // 缓存穿透-缓存空值处理：设置一个较短的过期时间，以防止真正的数据到来前一直使用空值
            redisService.setCacheObject(navigationCacheKey, Lists.newArrayList(), 1L, TimeUnit.SECONDS);
        } else {
            redisService.setCacheObject(navigationCacheKey, finalNavigationList1, 12L, TimeUnit.HOURS);
        }
        return finalNavigationList1;
    }

    private String buildNavigationCacheKey(Long userId) {
        return MessageFormat.format(RedisCacheKey.GOODS_NAVIGATION_LIST, userId.toString());
    }

    /**
     * 通过枚举构建数据
     *
     * @param finalNavigationList
     * @return
     */
    private List<GoodsCategory> buildResultDataByEnum(List<GoodsCategory> finalNavigationList) {
        List<GoodsCategory> finalNavigationList1 = Lists.newArrayList();
        for (GoodsStyleEnum goodsStyleEnum : GoodsStyleEnum.values()) {
            GoodsCategory goodsCategory = new GoodsCategory();
            finalNavigationList1.add(goodsCategory);
            goodsCategory.setStyle(goodsStyleEnum.getName());
            goodsCategory.setStyleDesc(goodsStyleEnum.getDesc());
        }
        finalNavigationList1.forEach(item -> {
            for (GoodsCategory goodsCategory : finalNavigationList) {
                if (goodsCategory.getStyle().equals(item.getStyle())) {
                    item.setBrands(goodsCategory.getBrands());
                }
            }
        });
        return finalNavigationList1;
    }

    /**
     * 商品查询分页列表
     *
     * @param req 分页列表入参
     * @return 分页列表
     */
    public IPage<GoodsDTO> goodsListPage(GoodsListReq req) {
        return queryGoodsPage(req);
    }

    public IPage<GoodsDTO> goodsRecommendPage(GoodsListReq req) {
        req.setHasRecommend(true);
        return queryGoodsPage(req);
    }

    private IPage<GoodsDTO> queryGoodsPage(GoodsListReq req) {
        Long userId = SecurityUtils.getUserId();
        // 构建查询参数
        buildListParam(req);
        String md5 = goodsCacheKey(req);
        String cacheKey = MessageFormat.format(RedisCacheKey.GOODS_PAGE, md5);
        if (Boolean.TRUE.equals(redisService.hasKey(cacheKey))) {
            JSONObject cacheValue = redisService.getCacheObject(cacheKey);
            if (Objects.isNull(cacheValue)) {
                IPage<GoodsDTO> activityResultIPage = new Page<>(1, 7, 0);
                redisService.setCacheObject(cacheKey, activityResultIPage, 10L, TimeUnit.SECONDS);
                return activityResultIPage;
            } else {
                return JSON.parseObject(cacheValue.toString(), new TypeReference<IPage<GoodsDTO>>() {
                });
            }
        }
        Page<GoodsDTO> customerPage = new Page<>(req.getPageNum(), req.getPageSize());
        IPage<GoodsDTO> activityResultIPage = goodsService.getGoodsByPage(customerPage, req);
        if (CollUtil.isNotEmpty(activityResultIPage.getRecords())) {
            activityResultIPage.getRecords().forEach(item -> processUnitPrice(userId, item));
            redisService.setCacheObject(cacheKey, activityResultIPage, 1L, TimeUnit.MINUTES);
        } else {
            redisService.setCacheObject(cacheKey, activityResultIPage, 10L, TimeUnit.SECONDS);
        }
        return activityResultIPage;
    }

    /**
     * 商品筛选条件查询
     * <p>
     *
     * @param req
     * @return
     */
    public GoodsFilterDTO goodsFilter(GoodsFilterReq req) {
        GoodsFilterDTO goodsFilterDTO = goodsService.queryGoodsFilter(req, SecurityUtils.getUserId());
        dealFilterResult(goodsFilterDTO);
        // 设置排序页面条件数据
        if (Objects.isNull(goodsFilterDTO)) {
            goodsFilterDTO = new GoodsFilterDTO();
        }
        goodsFilterDTO.setSortByShow(workBenchGoodsConfig.getSortByShow());
        return goodsFilterDTO;
    }

    /**
     * 查询商品详情及口味列表
     *
     * @param goodsId 商品ID
     * @return 商品详情及口味列表
     */
    public GoodsDTO detail(Long goodsId) {
        Goods goods = goodsService.getOne(new LambdaQueryWrapper<Goods>()
                .eq(Goods::getDeletedVersion, 0L)
                .eq(Goods::getListingStatus, GoodsListingStatusEnum.YES.getCode())
                .eq(Goods::getGoodsId, goodsId)
        );
        if (Objects.isNull(goods)) {
            return new GoodsDTO();
        }
        // 扩展信息追加
        return buildExternInfo(goodsId, goods);
    }

    /**
     * 导航数据查询
     *
     * @return
     */
    private List<GoodsCategory> doBuildNavigation(List<Goods> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        // 分组
        Map<String, List<Goods>> allDataMap = list.stream()
                .filter(item -> Objects.nonNull(item.getStyle()))
                .collect(Collectors.groupingBy(Goods::getStyle));

        List<GoodsCategory> finalNavigationList = new ArrayList<>();
        for (Map.Entry<String, List<Goods>> entry : allDataMap.entrySet()) {
            String style = entry.getKey();
            List<Goods> goodsList = entry.getValue();

            GoodsCategory goodsCategory = new GoodsCategory();
            goodsCategory.setStyle(style);
            finalNavigationList.add(goodsCategory);

            // 获取品牌列表
            List<GoodsBrand> brandList = new ArrayList<>();
            goodsCategory.setBrands(brandList);

            Map<String, List<Goods>> brandMap = goodsList.stream()
                    .filter(item -> Objects.nonNull(item.getBrandNo()))
                    .collect(Collectors.groupingBy(Goods::getBrandNo));

            for (Map.Entry<String, List<Goods>> brandEntry : brandMap.entrySet()) {
                String brandNo = brandEntry.getKey();
                List<Goods> brandGoodsList = brandEntry.getValue();

                GoodsBrand goodsBrand = new GoodsBrand();
                goodsBrand.setBrandNo(brandNo);
                brandList.add(goodsBrand);

                // 获取产品列表
                List<GoodsProduct> products = new ArrayList<>();
                goodsBrand.setProducts(products);

                try {
                    List<GoodsDTO> curGoodsList = GoodsMapping.INSTANCE.coverToDTOList(brandGoodsList);
                    for (GoodsDTO goodsDTO : curGoodsList) {
                        String styleAndBrandNo = GoodsUtil.buildStyleAndBrandNo(goodsDTO.getStyle(), goodsDTO.getBrandNo());
                        String currentStyleAndBrandNo = GoodsUtil.buildStyleAndBrandNo(style, brandNo);
                        if (styleAndBrandNo.equals(currentStyleAndBrandNo)) {
                            List<GoodsProduct> curGoodsList1 = GoodsMapping.INSTANCE.coverToProductList(Collections.singletonList(goodsDTO));
                            products.addAll(curGoodsList1);
                        }
                    }
                } catch (Exception e) {
                    // 处理异常
                    e.printStackTrace();
                }
            }
        }
        return finalNavigationList;
    }

    /**
     * 构建商品详情及口味列表
     *
     * @param goodsId
     * @param goods
     * @return
     */
    private GoodsDTO buildExternInfo(Long goodsId, Goods goods) {
        GoodsDTO goodsDTO = GoodsMapping.INSTANCE.coverToDTO(goods);
        // 价格处理
        processUnitPrice(SecurityUtils.getUserId(), goodsDTO);
        // 口味关联数据查询
        List<GoodsRelation> goodsRelationList = goodsRelationService.list(new LambdaQueryWrapper<GoodsRelation>()
                .eq(GoodsRelation::getGoodsId, goodsId)
                .eq(GoodsRelation::getDeletedVersion, 0L)
                .orderByAsc(GoodsRelation::getGoodsRelationId)
        );
        goodsDTO.setGoodsRelations(GoodsRelationMapping.INSTANCE.coverToVOList(goodsRelationList));
        // 素材展示数据查询
        List<GoodsMaterial> goodsMaterials = goodsMaterialService.list(new LambdaQueryWrapper<GoodsMaterial>()
                .eq(GoodsMaterial::getGoodsId, goodsId)
                .eq(GoodsMaterial::getDeletedVersion, 0L)
        );
        goodsDTO.setMaterials(GoodsMaterialMapping.INSTANCE.coverToVOList(goodsMaterials));
        // 获取商品描述信息
        List<GoodsDesc> descList = goodsDescService.lambdaQuery()
                .eq(GoodsDesc::getGoodsId, goodsId)
                .list();
        goodsDTO.setGoodsDescs(GoodsDescMapping.INSTANCE.coverToDTOList(descList));

        return goodsDTO;
    }

    /**
     * 构建查询参数
     *
     * @param req
     */
    private void buildListParam(GoodsListReq req) {
        req.setUserId(SecurityUtils.getUserId());
        req.setBrandNoList(buildQueryFieldForStr(req.getBrandNos()));
        req.setTasteNameList(buildQueryFieldForStr(req.getTasteNames()));
        req.setPuffNumList(buildQueryFieldForInt(req.getPuffNumbers()));
        req.setBatteryCapacityArr(buildQueryFieldForInt(req.getBatteryCapacityList()));
        req.setLiquidCapacityArr(buildQueryFieldForInt(req.getLiquidCapacityList()));
    }

    /**
     * 结果数据处理
     *
     * @param goodsFilterDTO 查询结果
     */
    private void dealFilterResult(GoodsFilterDTO goodsFilterDTO) {
        if (Objects.isNull(goodsFilterDTO)) {
            return;
        }
        // 查询字段处理
        goodsFilterDTO.setBrandNos(buildQueryFieldForStr(goodsFilterDTO.getBrandNo()));
        goodsFilterDTO.setBatteryCapacityList(buildQueryFieldForStr(goodsFilterDTO.getBatteryCapacity()));
        goodsFilterDTO.setLiquidCapacityList(buildQueryFieldForStr(goodsFilterDTO.getLiquidCapacity()));
        goodsFilterDTO.setTasteNames(buildQueryFieldForStr(goodsFilterDTO.getTasteName()));
        goodsFilterDTO.setPuffNumbers(buildQueryFieldForStr(goodsFilterDTO.getPuffNumber()));
    }

    /**
     * 构建查询字段
     *
     * @param
     * @return
     */
    private List<String> buildQueryFieldForStr(String inputParam) {
        List<String> batteryCapacityList = Lists.newArrayList();
        if (CharSequenceUtil.isNotBlank(inputParam)) {
            String[] batteryCapacityArr = inputParam.split(GoodsConstant.SPLIT_COMMA, -1);
            // 移除所有空字符串
            for (String s : batteryCapacityArr) {
                if (CharSequenceUtil.isNotBlank(s)) {
                    batteryCapacityList.add(s);
                }
            }
        }
        return batteryCapacityList;
    }

    private List<Integer> buildQueryFieldForInt(String inputParam) {
        List<Integer> batteryCapacityList = Lists.newArrayList();
        if (CharSequenceUtil.isNotBlank(inputParam)) {
            String[] batteryCapacityArr = inputParam.split(GoodsConstant.SPLIT_COMMA, -1);
            // 移除所有空字符串
            for (String s : batteryCapacityArr) {
                if (CharSequenceUtil.isNotBlank(s)) {
                    batteryCapacityList.add(Integer.valueOf(s));
                }
            }
        }
        return batteryCapacityList;
    }

    /**
     * 处理商品价格
     *
     * @param userId
     * @param item
     */
    private void processUnitPrice(Long userId, GoodsDTO item) {
        /**
         *
         *  此处逻辑调整： 客户是否认证&框架合同期限
         *  1、 游客模式，直接显示零售价
         *  2、首先肯定是登录客户
         *  3、如果客户是认证客户，则判断是否是合同期限，如果合同期限未到，则显示批发价，如果合同期限已到，则显示零售价
         *  4、客户是否认证&框架合同期限 --通过接口查询，并有一定缓存机制 ；但是还是会有数据一致性问题
         *
         */
        if (Objects.isNull(userId) || userId.compareTo(0L) == 0) {
            log.info("用户ID为空或0，直接返回零售价");
            return;
        }
        try {
            R<SysUser> sysUserR = remoteUserService.getSysUserInfoCacheById(userId);
            if (R.isError(sysUserR)) {
                log.warn("获取用户信息失败，返回零售价");
                return;
            }
            SysUser sysUser = sysUserR.getData();
            Date currentDate = new Date();
            log.debug("当前时间: {}", currentDate);
            log.debug("合同期限: {}", sysUser.getFrameworkContractDuration());
            boolean flag = Boolean.TRUE.equals(sysUser.isHasValid()) && Objects.nonNull(sysUser.getFrameworkContractDuration())
                && sysUser.getFrameworkContractDuration().compareTo(currentDate) > 0;
            if (flag) {
                log.info("用户已认证且合同期限未到，设置批发价");
                item.setUnitPrice(item.getWholesaleUnitPrice());
                item.setCurrentUnitPrice(item.getCurrentWholesaleUnitPrice());
            } else {
                log.info("用户未认证或合同期限已到，设置零售价");
            }
        } catch (Exception e) {
            log.error("处理商品价格时发生异常", e);
        }
    }

    /**
     * 商品管理列表分页查询
     *
     * @param req
     * @return
     */
    public PageResult<GoodsManageListDTO> goodsManagePage(GoodsManageListReq req) {
        Page<Goods> page = new Page<>(req.getPageNum(), req.getPageSize());
        Page<Goods> result = goodsService.lambdaQuery()
                .eq(req.getListingStatus() != null, Goods::getListingStatus, req.getListingStatus())
                .eq(Goods::getDeletedVersion, 0L)
                .orderByDesc(Goods::getGoodsId)
                .page(page);
        return PageUtil.success(GoodsMapping.INSTANCE.covertToManageDTOList(result.getRecords()), page.getTotal());
    }

    @Transactional
    public Long saveGoods(CreateOrUpdateGoodsReq req) {
        req.setGoodsId(null);
        // 检查产品编码
        List<String> productNos = req.getGoodsTasteList().stream()
                .map(CreateOrUpdateGoodsReq.GoodsTaste::getProductNo)
                .collect(Collectors.toList());
        checkProductNo(productNos);
        // 处理封面
        processGoodsCover(req);
        // 1. 保存 商品、图片、阶梯价、口味、描述等信息
        // 商品本体
        Goods entity = GoodsMapping.INSTANCE.convertToEntity(req);
        if (GoodsListingStatusEnum.YES.getCode().equals(entity.getListingStatus())) {
            checkGoodsCover(Collections.singletonList(entity));
        }
        goodsService.save(entity);
        Long goodsId = entity.getGoodsId();
        saveAdditionalInfo(goodsId, req);
        // 2. 记录商品操作日志
        GoodsListingStatusEnum statusEnum = GoodsListingStatusEnum.getByCode(req.getListingStatus());
        saveOperateLog(goodsId, "商品创建，上下架状态为：" + Optional.ofNullable(statusEnum).map(GoodsListingStatusEnum::getDesc).orElse("" + req.getListingStatus()));
        removeCache();
        return goodsId;
    }

    /**
     * 检查产品编码是否有效
     *
     * @param productNos
     */
    private void checkProductNo(Collection<String> productNos) {
        if (CollUtil.isEmpty(productNos)) {
            return;
        }
        List<U8Product> list = productService.lambdaQuery()
                .in(U8Product::getProductCode, productNos)
                .list();
        Set<String> dbCodes = list.stream()
                .map(U8Product::getProductCode)
                .collect(Collectors.toSet());
        Set<String> unKnowCode = productNos.stream()
                .filter(code -> !dbCodes.contains(code))
                .collect(Collectors.toSet());
        if (!unKnowCode.isEmpty()) {
            throw new ServiceException("产品编码无效" + unKnowCode + "，请检查");
        }
    }

    @Transactional
    public Long updateGoods(CreateOrUpdateGoodsReq req) {
        // 检查产品编码是否有效
        checkProductNo(req.getGoodsTasteList().stream().map(CreateOrUpdateGoodsReq.GoodsTaste::getProductNo).collect(Collectors.toList()));

        processGoodsCover(req);
        Long goodsId = req.getGoodsId();
        Goods dbGoods = goodsService.getById(goodsId);
        if (dbGoods == null) {
            throw new ServiceException("商品不存在：" + goodsId);
        }
        // 1. 保存 商品、图片、阶梯价、口味、描述等信息
        // 商品本体
        Goods entity = GoodsMapping.INSTANCE.convertToEntity(req);
        if (GoodsListingStatusEnum.YES.getCode().equals(entity.getListingStatus())) {
            checkGoodsCover(Collections.singletonList(entity));
        }
        goodsService.updateById(entity);

        // 获取原本的口味数据
        List<GoodsRelation> oldRelList = goodsRelationService.lambdaQuery()
                .eq(GoodsRelation::getGoodsId, goodsId)
                .list();

        // 清除原本关联的数据信息
        goodsMaterialService.lambdaUpdate().eq(GoodsMaterial::getGoodsId, goodsId).in(GoodsMaterial::getMaterialType, Arrays.asList(GoodsMaterialTypeEnum.PRODUCT_IMAGE.getCode(), GoodsMaterialTypeEnum.PRODUCT_VIDEO.getCode())).remove();
        priceTierService.lambdaUpdate().eq(PriceTier::getGoodsId, goodsId).remove();
        goodsRelationService.lambdaUpdate().eq(GoodsRelation::getGoodsId, goodsId).remove();
        goodsDescService.lambdaUpdate().eq(GoodsDesc::getGoodsId, goodsId).remove();
        // 重新保存数据
        saveAdditionalInfo(goodsId, req, oldRelList);
        // 2. 记录商品操作日志
        GoodsListingStatusEnum statusEnum = GoodsListingStatusEnum.getByCode(req.getListingStatus());
        saveOperateLog(goodsId, "商品更新，上下架状态为：" + Optional.ofNullable(statusEnum).map(GoodsListingStatusEnum::getDesc).orElse("" + req.getListingStatus()));
        removeCache();
        return goodsId;
    }

    /**
     * 处理商品封面图。封面图为空且商品图片不为空，则取商品图片的第一张作为封面图
     *
     * @param req
     */
    private void processGoodsCover(CreateOrUpdateGoodsReq req) {
        if (CollUtil.isNotEmpty(req.getGoodsMaterials())) {
            req.setCoverUrl(req.getGoodsMaterials().get(0).getMaterialUrl());
        }
    }

    private void saveAdditionalInfo(Long goodsId, CreateOrUpdateGoodsReq req) {
        saveAdditionalInfo(goodsId, req, null);
    }


    /**
     * 保存商品相关数据：图片、阶梯价、口味、描述等
     *
     * @param goodsId
     * @param req
     */
    private void saveAdditionalInfo(Long goodsId, CreateOrUpdateGoodsReq req, List<GoodsRelation> oldRelList) {
        // 商品图片
        List<GoodsMaterialReq> goodsImageUrl = req.getGoodsMaterials();
        log.info("素材数量:{}", goodsImageUrl.size());
        if (CollUtil.isNotEmpty(goodsImageUrl)) {
            if (goodsImageUrl.size() > workBenchGoodsConfig.getMaxGoodsMaterialNum()) {
                throw new ServiceException("商品素材不能超过" + workBenchGoodsConfig.getMaxGoodsMaterialNum() + "个");
            }
            goodsMaterialService.saveBatch(goodsImageUrl.stream().map(item -> {
                GoodsMaterial goodsMaterial = new GoodsMaterial();
                goodsMaterial.setGoodsId(goodsId);
                goodsMaterial.setMaterialType(GoodsMaterialTypeEnum.PRODUCT_IMAGE.getCode());
                if (StringUtils.isNotEmpty(item.getVideoUrl())) {
                    goodsMaterial.setMaterialType(GoodsMaterialTypeEnum.PRODUCT_VIDEO.getCode());
                    goodsMaterial.setVideoUrl(item.getVideoUrl());
                }
                goodsMaterial.setMaterialUrl(item.getMaterialUrl());
                return goodsMaterial;
            }).collect(Collectors.toList()));
        }
        // 商品阶梯价
        List<CreateOrUpdateGoodsReq.PriceTier> priceTierList = req.getPriceTierList();
        if (CollUtil.isNotEmpty(priceTierList)) {
            priceTierService.saveBatch(priceTierList.stream().map(item -> {
                PriceTier priceTier = PriceTierMapping.INSTANCE.convertToEntity(item);
                priceTier.setGoodsId(goodsId);
                priceTier.setCurrency(req.getCurrency());
                return priceTier;
            }).collect(Collectors.toList()));
        }
        // 商品口味
        List<CreateOrUpdateGoodsReq.GoodsTaste> goodsTasteList = req.getGoodsTasteList();
        if (CollUtil.isNotEmpty(goodsTasteList)) {
            // 如果名称相同，则保留口味ID不变
            Map<String, Long> tasteName2Id = oldRelList == null
                    ? new HashMap<>()
                    : oldRelList.stream().collect(Collectors.toMap(GoodsRelation::getTasteName, GoodsRelation::getGoodsRelationId, (v1, v2) -> v1));
            List<GoodsRelation> newRelList = goodsTasteList.stream().map(item -> {
                GoodsRelation goodsTaste = GoodsRelationMapping.INSTANCE.covertToEntity(item);
                goodsTaste.setGoodsRelationId(tasteName2Id.get(goodsTaste.getTasteName()));
                goodsTaste.setGoodsId(goodsId);
                return goodsTaste;
            }).collect(Collectors.toList());
            goodsRelationService.saveBatch(newRelList);
        }
        // 商品描述
        List<GoodsDesc> goodsDescList = new ArrayList<>();
        if (StringUtils.isNotEmpty(req.getGoodsDesc())) {
            goodsDescList.add(new GoodsDesc(null, goodsId, GoodsDescTypeEnum.DESC.getCode(), req.getGoodsDesc()));
        }
        if (StringUtils.isNotEmpty(req.getGoodsSpec())) {
            goodsDescList.add(new GoodsDesc(null, goodsId, GoodsDescTypeEnum.SPEC.getCode(), req.getGoodsSpec()));
        }
        if (CollUtil.isNotEmpty(goodsDescList)) {
            goodsDescService.saveBatch(goodsDescList);
        }
    }

    /**
     * 保存商品操作日志
     *
     * @param goodsId
     * @param content
     * @return
     */
    private Boolean saveOperateLog(Long goodsId, String content) {
        return goodsOperateLogService.save(buildOperateLog(goodsId, content));
    }

    /**
     * 批量保存商品操作日志
     *
     * @param goodsIds
     * @param content
     * @return
     */
    private Boolean saveBatchOperateLog(List<Long> goodsIds, String content) {
        return goodsOperateLogService.saveBatch(goodsIds.stream()
                .map(item -> buildOperateLog(item, content))
                .collect(Collectors.toList()));
    }

    private GoodsOperateLog buildOperateLog(Long goodsId, String content) {
        GoodsOperateLog operateLog = new GoodsOperateLog();
        operateLog.setGoodsId(goodsId);
        operateLog.setOperateUserId(SecurityUtils.getUserId());
        operateLog.setOperateUserName(SecurityUtils.getUsername());
        operateLog.setOperateTime(new Date());
        operateLog.setOperateContent(content);
        return operateLog;
    }

    /**
     * 商品管理-商品详情
     *
     * @param goodsId
     * @return
     */
    public GoodsManageDetailDTO goodsManageDetail(Long goodsId) {
        Goods goods = goodsService.getOne(new LambdaQueryWrapper<Goods>()
                .eq(Goods::getDeletedVersion, 0L)
                .eq(Goods::getGoodsId, goodsId)
        );
        if (Objects.isNull(goods)) {
            return new GoodsManageDetailDTO();
        }
        GoodsManageDetailDTO detailDTO = GoodsMapping.INSTANCE.covertToManageDetailDTO(goods);
        // 图片
        detailDTO.setGoodsMaterials(GoodsMaterialMapping.INSTANCE.coverToVOList(goodsMaterialService.lambdaQuery()
                .eq(GoodsMaterial::getGoodsId, goodsId)
                .in(GoodsMaterial::getMaterialType, Arrays.asList(GoodsMaterialTypeEnum.PRODUCT_IMAGE.getCode(), GoodsMaterialTypeEnum.PRODUCT_VIDEO.getCode()))
                .eq(GoodsMaterial::getDeletedVersion, 0)
                .list()));
        // 阶梯价
        detailDTO.setPriceTierList(PriceTierMapping.INSTANCE.covertToDTOList(
                priceTierService.lambdaQuery()
                        .eq(PriceTier::getGoodsId, goodsId)
                        .list()));
        // 口味
        detailDTO.setGoodsTasteList(GoodsRelationMapping.INSTANCE.covertToDTOList(
                goodsRelationService.lambdaQuery()
                        .eq(GoodsRelation::getGoodsId, goodsId)
                        .list()));
        // 商品描述
        List<GoodsDesc> descList = goodsDescService.lambdaQuery()
                .eq(GoodsDesc::getGoodsId, goodsId)
                .list();
        if (CollUtil.isNotEmpty(descList)) {
            descList.forEach(item -> {
                if (Objects.equals(item.getDescType(), GoodsDescTypeEnum.DESC.getCode())) {
                    detailDTO.setGoodsDesc(item.getDescContent());
                } else if (Objects.equals(item.getDescType(), GoodsDescTypeEnum.SPEC.getCode())) {
                    detailDTO.setGoodsSpec(item.getDescContent());
                }
            });
        }
        return detailDTO;
    }

    /**
     * 商品管理-删除商品
     *
     * @param goodsIds
     * @return
     */
    @Transactional
    public Boolean deleteGoods(List<Long> goodsIds) {
        if (CollUtil.isEmpty(goodsIds)) {
            return false;
        }
        Boolean result = goodsService.lambdaUpdate()
                .in(Goods::getGoodsId, goodsIds)
                .setSql("deleted_version = goods_id")
                .set(Goods::getUpdatedTime, new Date())
                .set(Goods::getUpdatedBy, SecurityUtils.getUserId())
                .update();
        saveBatchOperateLog(goodsIds, "删除商品");
        removeCache();
        return result;
    }

    /**
     * 商品管理-商品上架/下架
     *
     * @param req
     * @return
     */
    @Transactional
    public Boolean updateListingStatus(UpdateGoodsStatusReq req) {
        if (CollUtil.isEmpty(req.getGoodsIds())) {
            return false;
        }
        GoodsListingStatusEnum statusEnum = GoodsListingStatusEnum.getByCode(req.getListingStatus());
        if (Objects.isNull(statusEnum)) {
            return false;
        }
        // 241021 上架操作需要检查是否有商品封面
        if (GoodsListingStatusEnum.YES == statusEnum) {
            List<Goods> goodsList = goodsService.lambdaQuery()
                    .select(Goods::getGoodsId, Goods::getGoodsName, Goods::getCoverUrl)
                    .in(Goods::getGoodsId, req.getGoodsIds())
                    .list();
            checkGoodsCover(goodsList);
        }

        Boolean result = goodsService.lambdaUpdate()
                .in(Goods::getGoodsId, req.getGoodsIds())
                .set(Goods::getListingStatus, statusEnum.getCode())
                .set(Goods::getUpdatedTime, new Date())
                .set(Goods::getUpdatedBy, SecurityUtils.getUserId())
                .update();

        saveBatchOperateLog(req.getGoodsIds(), "修改上下架状态：" + statusEnum.getDesc());
        removeCache();
        return result;
    }

    /**
     * 241021 检查商品封面，如果未设置商品封面则报错
     *
     * @param goodsList
     */
    private void checkGoodsCover(List<Goods> goodsList) {
        List<Goods> noCoverGoodsList = goodsList.stream()
                .filter(item -> StringUtils.isEmpty(item.getCoverUrl()))
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(noCoverGoodsList)) {
            log.error("商品：{}未设置商品封面", noCoverGoodsList.stream().map(g -> g.getGoodsId().toString()).collect(Collectors.joining(",")));
            throw new ServiceException("商品：" + noCoverGoodsList.stream().map(Goods::getGoodsName).collect(Collectors.joining(",")) + "未设置商品封面");
        }
    }

    public List<GoodsManageListDTO> listRecommendGoods() {
        List<Goods> list = goodsService.lambdaQuery()
                .isNotNull(Goods::getRecommendSort)
                .eq(Goods::getDeletedVersion, 0L)
                .orderByAsc(Goods::getRecommendSort)
                .list();
        return GoodsMapping.INSTANCE.covertToManageDTOList(list);
    }

    @Transactional
    public Boolean updateRecommendGoods(List<Long> goodsIds) {
        if (goodsIds == null || goodsIds.isEmpty()) {
            return false;
        }
        List<Goods> goodsList = goodsService.lambdaQuery().in(Goods::getGoodsId, goodsIds).list();
        List<Goods> notListGoodsList = goodsList.stream()
                .filter(g -> !GoodsListingStatusEnum.YES.getCode().equals(g.getListingStatus()))
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(notListGoodsList)) {
            throw new ServiceException("商品：" + notListGoodsList.stream().map(Goods::getGoodsName).collect(Collectors.joining(",")) + "未上架，不能推荐");
        }
        // 清空推荐商品数据
        goodsService.lambdaUpdate().set(Goods::getRecommendSort, null).update();
        // 设置推荐商品数据
        Boolean result = goodsService.updateRecommendGoods(goodsIds);
        saveBatchOperateLog(goodsIds, "推荐商品");
        removeCache();
        return result;
    }

    public List<GoodsOperateLog> logInfo(Long goodsId) {
        return goodsOperateLogService.lambdaQuery()
                .eq(GoodsOperateLog::getGoodsId, goodsId)
                .orderByDesc(GoodsOperateLog::getOperateTime)
                .list();
    }

    /**
     * 商品批量保存
     *
     * @param goodsEOS
     * @return
     * @throws IOException
     */
    @Transactional
    public Boolean saveGoodsEO(List<GoodsEO> goodsEOS) {
        if (CollUtil.isEmpty(goodsEOS)) {
            return false;
        }
        // 校验产品编号
        checkProductNo(goodsEOS.stream().flatMap(g -> g.getGoodsTastes().stream()).map(GoodsTasteEO::getProductNo).collect(Collectors.toList()));
        // 导入场景不含附件
        // 需要处理 1、商品主体 2、商品描述 3、商品阶梯价 4、商品关联口味 5、商品日志

        List<Long> goodsIds = new ArrayList<>();
        List<Goods> goodsList = new ArrayList<>();
        List<GoodsDesc> goodsDescList = new ArrayList<>();
        List<PriceTier> priceTierList = new ArrayList<>();
        List<GoodsRelation> goodsRelationList = new ArrayList<>();
        for (GoodsEO goodsEO : goodsEOS) {
            long goodsId = IdWorker.getId();
            // 商品主体
            Goods goods = GoodsMapping.INSTANCE.convertToEntityByEO(goodsEO);
            goods.setGoodsId(goodsId);
            // 241021 商品默认下架
            goods.setListingStatus(GoodsListingStatusEnum.NO.getCode());
            goodsList.add(goods);
            goodsIds.add(goodsId);

            // 商品描述
            if (StringUtils.isNotEmpty(goodsEO.getGoodsDesc())) {
                goodsDescList.add(GoodsDesc.builder().goodsId(goodsId).descType(GoodsDescTypeEnum.DESC.getCode()).descContent(goodsEO.getGoodsDesc()).build());
            }
            if (StringUtils.isNotEmpty(goodsEO.getGoodsSpec())) {
                goodsDescList.add(GoodsDesc.builder().goodsId(goodsId).descType(GoodsDescTypeEnum.SPEC.getCode()).descContent(goodsEO.getGoodsSpec()).build());
            }

            // 商品阶梯价
            List<PriceTierEO> priceTiers = goodsEO.getPriceTiers();
            if (CollUtil.isNotEmpty(priceTiers)) {
                List<PriceTier> tierList = PriceTierMapping.INSTANCE.convertToEntityListByEO(priceTiers);
                tierList.forEach(item -> item.setGoodsId(goodsId));
                priceTierList.addAll(tierList);
            }

            // 商品关联口味
            List<GoodsTasteEO> goodsTastes = goodsEO.getGoodsTastes();
            if (CollUtil.isNotEmpty(goodsTastes)) {
                List<GoodsRelation> relationList = GoodsRelationMapping.INSTANCE.covertToEntityListByEO(goodsTastes);
                relationList.forEach(item -> item.setGoodsId(goodsId));
                goodsRelationList.addAll(relationList);
            }
        }

        // 批量保存
        if (CollUtil.isNotEmpty(goodsList)) {
            goodsService.saveBatch(goodsList);
        }
        if (CollUtil.isNotEmpty(goodsDescList)) {
            goodsDescService.saveBatch(goodsDescList);
        }
        if (CollUtil.isNotEmpty(priceTierList)) {
            priceTierService.saveBatch(priceTierList);
        }
        if (CollUtil.isNotEmpty(goodsRelationList)) {
            goodsRelationService.saveBatch(goodsRelationList);
        }
        // 移除缓存
        removeCache();
        return saveBatchOperateLog(goodsIds, "导入商品");

    }

    /**
     * 根据商品ID集合查询商品EO
     *
     * @param goodsIds
     * @return
     */
    public List<GoodsEO> listGoodsEO(List<Long> goodsIds) {
        List<GoodsEO> goodsEOList = new ArrayList<>();
        List<Goods> goodsList = goodsService.lambdaQuery()
                .in(Goods::getGoodsId, goodsIds)
                .list();
        List<GoodsDesc> descList = goodsDescService.lambdaQuery()
                .in(GoodsDesc::getGoodsId, goodsIds)
                .list();
        List<PriceTier> tierList = priceTierService.lambdaQuery()
                .in(PriceTier::getGoodsId, goodsIds)
                .list();
        List<GoodsRelation> relationList = goodsRelationService.lambdaQuery()
                .in(GoodsRelation::getGoodsId, goodsIds)
                .list();

        Map<Long, List<GoodsDesc>> id2Desc = descList.stream().collect(Collectors.groupingBy(GoodsDesc::getGoodsId));
        Map<Long, List<PriceTier>> id2Tier = tierList.stream().collect(Collectors.groupingBy(PriceTier::getGoodsId));
        Map<Long, List<GoodsRelation>> id2Relation = relationList.stream().collect(Collectors.groupingBy(GoodsRelation::getGoodsId));

        for (Goods goods : goodsList) {
            GoodsEO goodsEO = GoodsMapping.INSTANCE.convertToEO(goods);
            goodsEOList.add(goodsEO);

            List<GoodsDesc> descs = id2Desc.get(goods.getGoodsId());
            if (CollUtil.isNotEmpty(descs)) {
                goodsEO.setGoodsDesc(descs.stream().filter(item -> item.getDescType().equals(GoodsDescTypeEnum.DESC.getCode())).findFirst().map(GoodsDesc::getDescContent).orElse(""));
                goodsEO.setGoodsSpec(descs.stream().filter(item -> item.getDescType().equals(GoodsDescTypeEnum.SPEC.getCode())).findFirst().map(GoodsDesc::getDescContent).orElse(""));
            }

            goodsEO.setPriceTiers(PriceTierMapping.INSTANCE.covertToEOList(id2Tier.get(goods.getGoodsId())));
            goodsEO.setGoodsTastes(GoodsRelationMapping.INSTANCE.covertToEOList(id2Relation.get(goods.getGoodsId())));
        }
        return goodsEOList;
    }

    /**
     * 缓存清除
     * <p>
     * 1、商品范围分配 2、商品上下架 3、数据导入 4、商品删除 5、商品修改
     */
    public void removeCache() {
        Collection<String> keys = redisService.keys(RedisCacheKey.GOODS_PAGE_PREFIX_KEY + "*");
        if (CollUtil.isNotEmpty(keys)) {
            log.info("remove GOODS cache key: {}", keys);
            redisService.deleteObject(keys);
        }
        Collection<String> keys1 = redisService.keys(RedisCacheKey.GOODS_NAVIGATION_PREFIX + "*");
        if (CollUtil.isNotEmpty(keys1)) {
            log.info("remove NAVIGATION cache key: {}", keys1);
            redisService.deleteObject(keys1);
        }
        log.info("remove PRICE TIER CACHE key: {}", CacheBusinessConstants.PRICE_TIER_CACHE);
        redisService.deleteObject(CacheBusinessConstants.PRICE_TIER_CACHE);
    }

    /**
     * 缓存key 构建
     *
     * @param req
     * @return
     */
    private String goodsCacheKey(GoodsListReq req) {
        String userIdStr = Objects.nonNull(SecurityUtils.getUserId()) ? SecurityUtils.getUserId().toString() : "";
        String style = CharSequenceUtil.isBlank(req.getStyle()) ? "" : req.getStyle();
        String brandNos = CharSequenceUtil.isBlank(req.getBrandNos()) ? "" : req.getBrandNos();
        String tasteNames = CharSequenceUtil.isBlank(req.getTasteNames()) ? "" : req.getTasteNames();
        String puffNumbers = CharSequenceUtil.isBlank(req.getPuffNumbers()) ? "" : req.getPuffNumbers();
        String batteryCapacityList = CharSequenceUtil.isBlank(req.getBatteryCapacityList()) ? "" : req.getBatteryCapacityList();
        String liquidCapacityList = CharSequenceUtil.isBlank(req.getLiquidCapacityList()) ? "" : req.getLiquidCapacityList();
        String hasRecommendStr = Boolean.TRUE.equals(req.isHasRecommend()) ? Boolean.FALSE.toString() : Boolean.TRUE.toString();
        String orderByStr = CharSequenceUtil.isBlank(req.getOrderBy()) ? "" : req.getOrderBy();
        String keyWord = CharSequenceUtil.isBlank(req.getKeyWord()) ? "" : req.getKeyWord();
        return DigestUtil.md5Hex(req.getPageNum() + "_"
                + req.getPageSize() + "_"
                + userIdStr + "_"
                + style + "_"
                + brandNos + "_"
                + tasteNames + "_"
                + puffNumbers + "_"
                + batteryCapacityList + "_"
                + liquidCapacityList + "_"
                + hasRecommendStr + "_"
                + orderByStr + "_"
                + keyWord
        );
    }

    /**
     * 根据关键字查询商品口味数据
     *
     * @param brandNo
     * @param keyWord
     * @param size
     * @return
     */
    public List<GoodsTasteResp> listGoodsTaste(String brandNo, String keyWord, Integer size) {
        return goodsRelationService.listGoodsTaste(brandNo, keyWord, size);
    }
}
